/**
 * (c) Copyright Ascensio System SIA 2025
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zzvcom.office.sdk.service;

import com.google.gson.Gson;
import com.onlyoffice.manager.document.DocumentManager;
import com.onlyoffice.manager.security.JwtManager;
import com.onlyoffice.manager.settings.SettingsManager;
import com.onlyoffice.model.common.User;
import com.onlyoffice.model.documenteditor.Config;
import com.onlyoffice.model.documenteditor.config.EditorConfig;
import com.onlyoffice.model.documenteditor.config.document.Info;
import com.onlyoffice.model.documenteditor.config.document.Permissions;
import com.onlyoffice.model.documenteditor.config.document.ReferenceData;
import com.onlyoffice.model.documenteditor.config.document.Type;
import com.onlyoffice.model.documenteditor.config.document.permissions.CommentGroups;
import com.onlyoffice.model.documenteditor.config.editorconfig.*;
import com.onlyoffice.model.documenteditor.config.editorconfig.customization.Close;
import com.onlyoffice.model.documenteditor.config.editorconfig.customization.Customer;
import com.onlyoffice.model.documenteditor.config.editorconfig.customization.Features;
import com.onlyoffice.model.documenteditor.config.editorconfig.customization.Goback;
import com.onlyoffice.model.documenteditor.config.editorconfig.embedded.Toolbar;
import com.onlyoffice.service.documenteditor.config.DefaultConfigService;
import com.zzvcom.office.cfg.CustomLogo;
import com.zzvcom.office.documentserver.models.enums.Action;
import com.zzvcom.office.documentserver.storage.FileStoragePathBuilder;
import com.zzvcom.office.entities.Group;
import com.zzvcom.office.entities.OfficeFile;
import com.zzvcom.office.entities.Permission;
import com.zzvcom.office.exception.OfficeException;
import com.zzvcom.office.sdk.manager.UrlManager;
import com.zzvcom.office.services.OfficeFileServices;
import com.zzvcom.office.services.UserServices;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class ConfigServiceImpl extends DefaultConfigService implements ConfigService {


    @Autowired
    private UserServices userService;

    @Autowired
    private FileStoragePathBuilder storagePathBuilder;

    @Autowired
    private UrlManager urlManager;
    @Autowired
    private OfficeFileServices officeFileServices;

    public ConfigServiceImpl(final DocumentManager documentManager, final UrlManager argUrlManager,
                             final JwtManager jwtManager, final SettingsManager settingsManager) {
        super(documentManager, argUrlManager, jwtManager, settingsManager);
    }

    @Override
    public Config createConfig(final String fileId, final Action action, final Type type) {
        com.zzvcom.office.entities.User appUser = userService.getCurrentUser();
        Action currentAction = action;
        String fileName = getDocumentManager().getDocumentName(fileId);
        if (currentAction == null) {
            currentAction = Action.edit;
        }
        Boolean isEditable = getDocumentManager().isEditable(fileName);
        if ((!isEditable && currentAction.equals(Action.edit) || currentAction.equals(Action.fillForms))
                && getDocumentManager().isFillable(fileName)) {
            isEditable = true;
            currentAction = Action.fillForms;
        }

        Mode mode = isEditable && !currentAction.equals(Action.view) ? Mode.EDIT : Mode.VIEW;
        Config config = super.createConfig(fileId, mode, type);
        EditorConfig editorConfig = config.getEditorConfig();
        if (!currentAction.equals(Action.view)
                && appUser.getPermissions().getSubmitForm()) {
            editorConfig.getCustomization().setSubmitForm(true);
        }
        OfficeFile officeFile = officeFileServices.getById(fileId);
        if (officeFile != null) {
            config.getDocument().setTitle(officeFile.getTitle());
        }
        Permissions permissions = config.getDocument().getPermissions();
        permissions = updatePermissions(permissions, currentAction, isEditable);
        //add by action yujikuan
        if (Action.view.equals(action)) {

            permissions.setEdit(false);
            permissions.setFillForms(false);
            permissions.setReview(false);
            permissions.setEditCommentAuthorOnly(false);
            permissions.setDeleteCommentAuthorOnly(false);
            permissions.setModifyContentControl(false);
            permissions.setModifyFilter(false);
            permissions.setRename(false);
            editorConfig.setPlugins(null);
            //浏览的时候不显示新建
            editorConfig.setCreateUrl(null);
            editorConfig.setTemplates( null);

        }
        config.getDocument().setPermissions(permissions);

        if (appUser.getName().equals("Anonymous")) {
            editorConfig.setCreateUrl(null);
        }

        if (getSettingsManager().isSecurityEnabled()) {
            config.setToken(getJwtManager().createToken(config));
        }

        return config;
    }


    @Override
    public ReferenceData getReferenceData(final String fileId) {
        Gson gson = new Gson();

        HashMap<String, String> fileKey = new HashMap<>();
        fileKey.put("fileName", getDocumentManager().getDocumentName(fileId));
        try {
            fileKey.put("userAddress", InetAddress.getLocalHost().getHostAddress());
        } catch (Exception e) {
            e.printStackTrace();
        }
        ReferenceData referenceData = ReferenceData.builder()
                .instanceId(storagePathBuilder.getServerUrl(true))
                .fileKey(gson.toJson(fileKey))
                .build();

        return referenceData;
    }

    @Override
    public Info getInfo(final String fileId) {
        OfficeFile officeFile = officeFileServices.getById(fileId);
        if (officeFile == null) {
            throw OfficeException.valueOf("文件不存在,fileId=" + fileId);
        }
        return Info.builder()
                .owner("yujikuan")
                //.favorite(false)
                .uploaded(officeFile.getCreateTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
                .build();
    }

    @Override
    public Permissions getPermissions(final String fileId) {
        com.zzvcom.office.entities.User appUser = userService.getCurrentUser();
        if (appUser == null) {
            return null;
        }
        Permission userPermissions = appUser.getPermissions();
        return Permissions.builder()
                //用于描述一个控制文档中聊天功能是否启用的配置项。当聊天权限设置为"true"时，"Chat"菜单按钮会显示出来，默认值为"true"。
                .chat(userPermissions.getChat())
                //功能说明
                //该配置项用于定义文档是否启用评论功能：
                //当设置为true时，文档侧边栏将显示"Comment"菜单选项
                //评论功能仅在编辑器模式为"edit"时可用（通过EditorConfig.mode参数控制）
                //默认值与Permissions.edit参数的值保持一致
                //使用场景
                //这个配置通常用于：
                //控制特定用户或用户组是否可以在文档中添加评论
                //实现文档的只读模式（禁用评论功能）
                //根据用户权限动态控制评论功能的可用性
                .comment(userPermissions.getComment())
                //评论组功能
                .commentGroups(getCommentGroups(userPermissions))
                //是否可以复制
                //该配置项定义了用户能否将文档内容复制到系统剪贴板：
                //当设置为true时（默认值）：用户可以正常复制文档内容到剪贴板，并粘贴到其他应用程序中
                //当设置为false时：用户只能在当前文档编辑器内部进行复制粘贴操作，无法将内容复制到外部应用程序
                .copy(userPermissions.getCopy())
                //控制文档是可下载还是仅支持在线查看/编辑
                //当下载权限设为"false"时，"File"菜单中将不显示"Download as..."选项
                //默认权限值为"true"（允许下载）
                //这通常用于文档管理系统或在线协作平台的权限控制功能。
                .download(userPermissions.getDownload())
                //定义了一个文档编辑权限的配置项。当设置为"true"时，文档可编辑且"文件"菜单会显示"编辑文档"选项；当设置为"false"时，文档只能查看，无法切换到编辑模式。默认值为"true"。
                .edit(userPermissions.getEdit())
                //一个表单填写权限配置项的功能：
                //控制文档表单是否可填写
                //仅当编辑器模式设置为"edit"时生效
                //默认值与edit或review权限参数值一致
                //属于文档编辑器配置的一部分
                .fillForms(userPermissions.getFillForms())
                //用于控制文档编辑器中内容控件设置是否可以被修改。只有当编辑器模式设置为"edit"时，
                // 此功能才可用。默认值为true，表示默认允许修改内容控件。文本内容控件，复选框控件，列表控件，下拉列表控件，日期控件，时间控件，图片控件，签名控件，超链接控件，表格控件，超链接控件，公式控件。
                .modifyContentControl(userPermissions.getModifyContentControl())
                //描述了一个过滤器权限配置项，用于控制电子表格编辑器中过滤器的应用范围。
                //功能说明
                //该配置项定义了过滤器的两种应用模式：
                //全局模式（true）：过滤器影响所有用户，当一个用户应用过滤器时，其他用户也会看到相同的结果
                //本地模式（false）：过滤器仅影响当前用户，其他用户不受影响
                //使用条件
                //过滤器修改功能仅在以下条件下可用：
                //EditorConfig.mode参数必须设置为"edit"模式
                //默认行为
                //默认值为true，即过滤器默认是全局应用的。
                //应用场景
                //这种配置在协作编辑电子表格时特别有用：
                //全局过滤：适用于需要所有用户查看相同数据子集的场景
                //个人过滤：适用于每个用户需要根据自己的需求查看不同数据的场景
                //协作意义
                //通过控制过滤器的应用范围，可以实现更灵活的数据查看和分析协作模式，满足不同团队和业务场景的需求
                .modifyFilter(userPermissions.getModifyFilter())
                //用于控制文档是否可打印
                .print(userPermissions.getPrint())
                .protect(userPermissions.getProtect())
                .review(userPermissions.getReview())
                //控制文档是否可以被审阅
                //当设置为"true"时，文档状态栏会显示"Review"菜单选项
                //文档审阅功能仅在编辑器模式为"edit"时可用
                .reviewGroups(getReviewGroups(userPermissions))
                //这段代码定义了一个私有成员变量 userInfoGroups，用于控制在编辑器中显示哪些用户组的信息。具体功能包括：
                //控制编辑器头部显示的用户名列表
                //控制输入时显示的用户光标和姓名提示
                //控制严格协作编辑模式下锁定对象时显示的用户名
                //该变量为字符串列表类型，不同的值有不同的显示效果：
                //["Group1", ""] 表示显示Group1组用户和无组用户的信
                .userInfoGroups(getUserInfoGroups(userPermissions))
                .build();
    }

    @Override
    public List<Template> getTemplates(final String fileId) {
        com.zzvcom.office.entities.User appUser = userService.getCurrentUser();

        if (!appUser.getName().equals("Anonymous")) {

            String fileName = getDocumentManager().getDocumentName(fileId);

            return List.of(
                    Template.builder()
                            .image("")
                            .title("空白")
                            .url(getUrlManager().getCreateUrl(fileId))
                            .build(), // create a blank template
                    Template.builder()
                            .image(urlManager.getTemplateImageUrl(fileName))
                            .title("样例文档")
                            .url(urlManager.getCreateSampleUrl(fileId))
                            .build()// create a template with sample content using the template image
            );
        }

        return null;
    }

    @Override
    public User getUser() {
        com.zzvcom.office.entities.User appUser = userService.getCurrentUser();

        if (appUser == null) {
            return null;
        }

        User user = User.builder()
                .id(String.valueOf(appUser.getId()))
                .name(appUser.getName())
                .group(appUser.getGroup().getName())
                .build();

        if (appUser.getAvatar()) {
            user.setImage(storagePathBuilder.getServerUrl(true)
                    + "/css/img/uid-"
                    + appUser.getId()
                    + ".png"
            );
        }

        return user;
    }

    Customer customer = new Customer();
    CustomLogo customLogo = new CustomLogo();

    //仅仅在开发版有效
    @PostConstruct
    public void initPost() {
        //开发 版才行
        /* customer.setName("XXX");
         customer.setInfo("FF");
         customer.setLogo("");
         customer.setLogoDark("");
         customer.setMail("FF");
         customer.setPhone("FF");
         customer.setWww("XX");*/

     /* String logoUrl="http://localhost:4000/tryoffice/static/css/img/logo.png";
         customLogo.setVisible(false);
         customLogo.setImage(logoUrl);
         customLogo.setImageLight(logoUrl);
         customLogo.setImageDark(logoUrl);
         customLogo.setImageEmbedded(logoUrl);
         customLogo.setUrl("https://zzvcom.com");*/
    }


    @Override
    public Customization getCustomization(final String fileId) {
        com.zzvcom.office.entities.User appUser = userService.getCurrentUser();
        Goback goback = Goback.builder()
                .url(getUrlManager().getGobackUrl(fileId))
                .build();
        if (appUser != null && appUser.getGoback() != null) {
            goback.setText(appUser.getGoback().getText());
            goback.setBlank(appUser.getGoback().getBlank());
        } else {
            goback.setUrl("");
        }
        // 此处的goback.setUrl("")表示返回按钮的链接地址为空，即不显示返回按钮
        goback.setUrl("");
        Close close = Close.builder().build();
        if (appUser != null && appUser.getClose() != null) {
            close.setText(appUser.getClose().getText());
            close.setVisible(appUser.getClose().getVisible());
        }
        Customization customization = Customization.builder()
                .autosave(true) // if the Autosave menu option is enabled or disabled
                .comments(false) // if the Comments menu button is displayed or hidden
                .compactHeader(true) /* if the additional action buttons are displayed
    in the upper part of the editor window header next to the customLogo (false) or in the toolbar (true) */
                .compactToolbar(true) // if the top toolbar type displayed is full (false) or compact (true)
                .forcesave(false)/* add the request for the forced file saving to the callback handler
    when saving the document within the document editing service */
                .help(false)  //  if the Help menu button is displayed or hidden
                .hideRightMenu(true) // if the right menu is displayed or hidden on first loading
                .hideRulers(false) // if the editor rulers are displayed or hidden
                .feedback(false)
                .plugins(false)//隐藏插件显示
                .toolbarHideFileName(false)//显示文件名字
                .toolbarNoTabs(false)
                // 可选值：modern-light、modern-dark、classic-light、dark-contrast、gray
                .uiTheme("modern-light")
                //.integrationMode("embed")
                .goback(goback)
                .close(close)
                .logo(customLogo)
                .customer(customer)
                //.zoom(-2)
                .features(Features.builder().spellcheck(false).build())
                .build();

        return customization;
    }

    @Override
    public CoEditing getCoEditing(final String fileId, final Mode mode, final Type type) {
        com.zzvcom.office.entities.User appUser = userService.getCurrentUser();

        if (mode.equals(Mode.VIEW) && appUser.getName().equals("Anonymous")) {
            return CoEditing.builder()
                    .mode(com.onlyoffice.model.documenteditor.config.editorconfig.coediting.Mode.STRICT)
                    .change(false)
                    .build();
        }

        return null;
    }

    @Override
    public Embedded getEmbedded(final String fileId) {
        String url = getUrlManager().getFileUrl(fileId);

        return Embedded.builder()
                //.embedUrl(url)
                .saveUrl(url)
               // .shareUrl(url)
                .toolbarDocked(Toolbar.TOP)
                .build();
    }

    /**
     * 评论组权限设置
     *
     * @param userPermissions
     * @return
     * @see com.onlyoffice.model.documenteditor.config.document.permissions.CommentGroups
     * CommentGroups的类，用于表示用户对文档中不同用户组评论的编辑、删除和查看权限。
     * 通过edit、remove和view三个列表属性，分别控制用户可以操作的评论所属用户组。空列表或null值表示无权限，
     * 特殊值[""]表示可操作不属于任何组的用户评论。
     */
    private CommentGroups getCommentGroups(final Permission userPermissions) {
        CommentGroups commentGroups = new CommentGroups();

        List<String> edit = userPermissions.getCommentsEditGroups().stream()
                .filter(commentGroup -> !commentGroup.getName().equals("NULL"))
                .map(Group::getName)
                .collect(Collectors.toList());

        if (edit != null && edit.size() > 0) {
            commentGroups.setEdit(edit);
        }

        List<String> view = userPermissions.getCommentsViewGroups().stream()
                .filter(commentGroup -> !commentGroup.getName().equals("NULL"))
                .map(Group::getName)
                .collect(Collectors.toList());

        if (view != null && view.size() > 0) {
            commentGroups.setView(view
            );
        }

        List<String> remove = userPermissions.getCommentsRemoveGroups().stream()
                .filter(commentGroup -> !commentGroup.getName().equals("NULL"))
                .map(Group::getName)
                .collect(Collectors.toList());

        if (remove != null && remove.size() > 0) {
            commentGroups.setRemove(remove);
        }

        return commentGroups;
    }

    private List<String> getReviewGroups(final Permission userPermissions) {
        List<String> reviewGroups = new ArrayList<>();

        if (userPermissions.getReviewGroups() != null) {
            reviewGroups = userPermissions.getReviewGroups().stream()
                    .filter(commentGroup -> !commentGroup.getName().equals("NULL"))
                    .map(Group::getName)
                    .collect(Collectors.toList());
        }

        return reviewGroups != null && reviewGroups.size() > 0 ? reviewGroups : null;
    }

    private List<String> getUserInfoGroups(final Permission userPermissions) {
        List<String> userInfoGroups = new ArrayList<>();

        if (userPermissions.getUserInfoGroups() != null) {
            userInfoGroups = userPermissions.getUserInfoGroups().stream()
                    .filter(commentGroup -> !commentGroup.getName().equals("NULL"))
                    .map(Group::getName)
                    .collect(Collectors.toList());
        }

        return userInfoGroups != null && userInfoGroups.size() > 0 ? userInfoGroups : null;
    }

    private Permissions updatePermissions(final Permissions permissions, final Action action,
                                          final Boolean isEditable) {
        permissions.setComment(
                !action.equals(Action.view)
                        && !action.equals(Action.fillForms)
                        && !action.equals(Action.embedded)
                        && !action.equals(Action.blockcontent)
        );

        permissions.setFillForms(
                !action.equals(Action.view)
                        && !action.equals(Action.comment)
                        && !action.equals(Action.blockcontent)
        );

        permissions.setReview(isEditable
                && (action.equals(Action.review) || action.equals(Action.edit)));

        permissions.setEdit(isEditable
                && (action.equals(Action.view)
                || action.equals(Action.edit)
                || action.equals(Action.filter)
                || action.equals(Action.blockcontent)));

        return permissions;
    }
}
